package utils

import (
	"context"
	"encoding/json"
	"errors"
	"time"

	"fmt"

	"github.com/flipped-aurora/gin-vue-admin/server/utils"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"main.go/global"
	"main.go/model/common"
)

const (
	UserTokenKey = "token_%s"
	TokenSet     = "user_token_set_%s"
)

func SetToken(info common.TokenInfo) (err error) {
	ctx := context.Background()
	// 删除旧token,实现单点登录
	if !global.EVA_CONFIG.System.UseMultipoint {
		keysToDelete, err := global.EVA_REDIS.SMembers(ctx, fmt.Sprintf(TokenSet, info.UUID)).Result()
		if err != nil {
			global.EVA_LOG.Error("Redis Imp SMembers error", zap.Error(err))
			return err
		}
		pipe := global.EVA_REDIS.Pipeline()
		for _, token := range keysToDelete {
			pipe.Del(ctx, fmt.Sprintf(UserTokenKey, token))
		}
		pipe.Del(ctx, fmt.Sprintf(TokenSet, info.UUID))
		_, err = pipe.Exec(ctx)
		if err != nil {
			global.EVA_LOG.Error("Redis Imp pipe error", zap.Error(err))
			return err
		}
	}

	tokenTimeout, _ := utils.ParseDuration(global.EVA_CONFIG.JWT.ExpiresTime)
	// 设置新token,token为key,info为value
	marshal, err := json.Marshal(info)
	if err != nil {
		global.EVA_LOG.Error("json.Marshal error", zap.Error(err))
		return err
	}
	err = global.EVA_REDIS.Set(ctx, fmt.Sprintf(UserTokenKey, info.Token), marshal, tokenTimeout).Err()
	if err != nil {
		global.EVA_LOG.Error("Redis Imp SetToken error ", zap.Error(err))
		return err
	}
	// 将新token添加到用户token集合,userid为key,token为valuw
	err = global.EVA_REDIS.SAdd(ctx, fmt.Sprintf(TokenSet, info.UUID), info.Token).Err()
	if err != nil {
		global.EVA_LOG.Error("Redis Imp SAdd error ", zap.Error(err))
		return err
	}
	return nil
}

// DeleteToken 从Redis中删除指定的token和与之相关联的用户token集合中的token。
// ctx: 上下文，用于控制函数执行期间的取消、超时等。
// info: 包含需要删除的token信息和相关联的用户ID。
// 返回值: 如果执行过程中出现错误，将返回错误信息；否则返回nil。
func DeleteToken(info common.TokenInfo) (err error) {
	ctx := context.Background()
	err = global.EVA_REDIS.Del(ctx, fmt.Sprintf(UserTokenKey, info.Token)).Err() // 从Redis中删除指定的token
	if err != nil {
		global.EVA_LOG.Error("Redis Imp DeleteToken error ", zap.Error(err)) // 记录删除token失败的日志
		return err
	}

	err = global.EVA_REDIS.SRem(ctx, fmt.Sprintf(TokenSet, info.UUID), info.Token).Err() // 从用户token集合中移除token
	if err != nil {
		global.EVA_LOG.Error("Redis Imp SRemToken error ", zap.Error(err)) // 记录从集合中移除token失败的日志
		return err
	}
	return nil
}

// ctx: 上下文，用于控制函数执行期间的取消、超时等。
// token: 用户的令牌，用于标识和验证用户。
// 返回值 info: 模型中的TokenInfo结构体，包含用户的相关信息。
// 返回值 err: 错误信息，如果执行过程中无错误发生，则为nil。
func GetUserinfoByToken(token string) (info common.TokenInfo, err error) {
	ctx := context.Background()
	key := fmt.Sprintf(UserTokenKey, token)
	// 从Redis获取存储的用户token信息
	result := global.EVA_REDIS.Get(ctx, key)
	err = result.Err() // 检查Redis操作是否出错
	if err != nil {
		// 如果有错误，记录日志并返回错误信息
		global.EVA_LOG.Error("Redis Imp GetUserinfoByToken error ", zap.Error(err))
		return info, err
	}

	// 将从Redis获取的JSON字符串解析为TokenInfo结构体
	err = json.Unmarshal([]byte(result.Val()), &info)
	if err != nil {
		// 如果解析出错，记录日志并返回错误信息
		global.EVA_LOG.Error("Redis Imp Unmarshal error ", zap.Error(err))
		return info, err
	}

	// 获取键的精确剩余生存时间（毫秒）
	pttl, err := global.EVA_REDIS.PTTL(ctx, key).Result()
	if err != nil {
		return info, err
	}
	global.EVA_LOG.Debug("token time", zap.Duration("milliseconds", pttl))
	if pttl > 0 {
		pill := time.Duration(pttl)
		global.EVA_LOG.Debug("token time", zap.Any("at", time.Duration(pttl)))
		bufferTime, _ := utils.ParseDuration(global.EVA_CONFIG.JWT.BufferTime)
		if pill < bufferTime {
			tokenTimeout, _ := utils.ParseDuration(global.EVA_CONFIG.JWT.ExpiresTime)
			global.EVA_LOG.Debug("token time", zap.Any("new time at", tokenTimeout))
			// 使用 Expire 方法更新键的过期时间
			err := global.EVA_REDIS.Expire(ctx, key, tokenTimeout).Err()
			if err != nil {
				// 处理错误
				global.EVA_LOG.Error("Error updating key TTL", zap.Error(err))
				return info, err
			}
		}

	}

	// 如果一切正常，返回解析后的用户信息和nil作为错误信息
	return info, err
}

func GetClaims(c *gin.Context) (info common.TokenInfo, err error) {
	token := c.Request.Header.Get("Authorization")

	if len(token) == 0 {
		global.EVA_LOG.Error("不存在存在Authorization")
		return info, errors.New("不存在存在Authorization")
	}
	info, err = GetUserinfoByToken(token)
	if err != nil {
		global.EVA_LOG.Error("Authorization解析失败", zap.Error(err))
		return info, errors.New("Authorization接新失败")
	}
	return info, err
}

// GetUserID 从Gin的Context中获取从jwt解析出来的用户ID
func GetUserID(c *gin.Context) uint {
	claims, exists := c.Get("claims")
	if claims == nil {
		if cl, err := GetClaims(c); err != nil {
			return 0
		} else {
			return uint(cl.UserID)
		}
	}
	if exists {
		if cl, err := GetClaims(c); err != nil {
			return 0
		} else {
			return uint(cl.UserID)
		}
	} else {
		waitUse := claims.(*common.TokenInfo)
		return uint(waitUse.UserID)
	}
}
